// ==UserScript==
// @name        豆包云盘视频优化浏览
// @namespace   Violentmonkey Scripts
// @match       https://www.doubao.com/*
// @require     https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.slim.min.js
// @require     https://cdn.jsdelivr.net/npm/lodash@4
// @require     https://gitee.com/viocha/gm-util/raw/main/loader.js?124234jlfadflj
// @require     https://unpkg.com/xgplayer@latest/dist/index.min.js
// @resource    playerCss https://unpkg.com/xgplayer@latest/dist/index.min.css
// @version     1.0.0
// @author      viocha
// @description 2025/5/12 21:40:07
// @run-at      document-start
// @top-level-await
// @inject-into page
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_listValues
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_getResourceURL
// @grant GM_getResourceText
// @grant GM_addElement
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant GM_openInTab
// @grant GM_setClipboard
// @grant GM.xmlHttpRequest
// @grant GM.download
// ==/UserScript==

// language=css
// GM_addStyle(`
// `);

// 保存原始的 XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.open = function(method, url, async, user, password){
	this._targetUrl = url;
	this._method = method;
	return originalOpen.apply(this, arguments);
};

XMLHttpRequest.prototype.send = function(body){
	if (this._method==='POST' && this._targetUrl.includes('https://www.doubao.com/samantha/media/get_play_info')){
		const xhr = this;
		const onLoad = function(){
			console.log('拦截到目标请求：', xhr._targetUrl);
			const json = xhr.responseText;
			const data = JSON.parse(json);
			const videoUrl = data.data.original_media_info.main_url;
			console.log('视频地址：', videoUrl);
			replaceVideo(videoUrl);
		};
		
		this.addEventListener('load', onLoad);
	}
	
	return originalSend.apply(this, arguments);
};

addStyle();

async function replaceVideo(url){
	// 添加播放器样式
	addStyle();
	
	// 移除已经播放的视频
	await waitFor(()=>$('.semi-portal:has(.semi-modal-body video)').length, {timeout:5000});
	$('xg-bar xg-icon .semi-icon')._click(); // 必须点击按钮，否则会后台播放
	await waitFor(()=>!$('.semi-portal:has(.semi-modal-body video)').length, {timeout:5000});
	
	// 播放器容器
	const $container = $(`<div id="video-container"></div>`);
	// 关闭按钮
	const $closeBtn = $(`<button class="close-btn">X</button>`).on('click', ()=>{
		$container.remove();
	});
	// 播放器元素
	const $player = $(`<div id="db-video"></div>`);
	const $videoWrapper = $(`<div class="video-wrapper"></div>`).append($player, $closeBtn);
	// 添加到页面
	$container.append($videoWrapper)
						.appendTo('body');
	
	// 播放器配置
	const config = {
		id:'db-video',
		url:url,
		autoplay:true, // 自动开始播放
		volume:1, // 音量最大
		cssFullscreen:false, // 禁用css全屏
		miniprogress:true, // 当控制栏隐藏时，显示底部的小进度条
		fluid:true, // 启用后，不会超出屏幕大小
		playbackRate:range(3, 1, {step:-0.25, eq:true}), // 播放速度
		mobile:{
			disablePress:false, // 开启长按倍速
			pressRate:3, // 长按3倍速
			gestureY:false, // 禁用手势调节亮度音量
			disableSeekIcon:true, // 禁用seek图标
			disableTimeProgress:true, // 禁用时间进度条
		},
	};
	// 加载播放器
	new Player(config);
	
	// =====================手机端自动横屏=============================
	const $controls = $('#db-video > xg-controls');
	document.addEventListener('fullscreenchange', ()=>{
		if (document.fullscreenElement!==null){ // 处于全屏状态
			screen.orientation.lock('landscape'); // 强制横屏
			$controls.css('position', 'fixed');   // 解决控制栏默认会偏移
		} else {
			$controls.css('position', 'absolute');
		}
	});
	
}

function addStyle(){
	if (!$('#player-style').length){
		const css = GM_getResourceText('playerCss');
		$(`<style id="player-style">${css}</style>`).appendTo('head');
		
		// ====================== 播放器布局 =============================
		// language=css
		GM_addStyle(`
      #video-container {
        position         : fixed;
        top              : 0;
        left             : 0;
        z-index          : 9999;
        width            : 100%;
        height           : 100%;
        background-color : rgba(175, 175, 175, 0.5);
      }

      .video-wrapper {
        position   : absolute;
        left       : 5%;
        width      : 90%;
        top        : 10%;
        max-height : 90%;
      }

      #video-container .close-btn {
        color         : gray;
        position      : absolute;
        top           : -3em;
        right         : -3em;
        width         : 2em;
        height        : 2em;
        border        : 1px solid lightgray;
        opacity       : 0.8;
        border-radius : 50%;
      }

      #video-container .close-btn:hover {
        opacity : 0.9;
      }

      #video-container .close-btn:active {
        opacity : 1;
      }

      /* 手机端，宽度为100% */
      @media (max-width : 500px) {
        .video-wrapper {
          width : 100%;
          left  : 0;
        }

        #video-container .close-btn {
          right : 0;
        }
      }

      /* 显示更全的文件名 */
      [data-testid="ai_space_file_item_label"] {
        font-size   : 12px !important;
        line-height : 1.2 !important;
        white-space : normal !important; /* 允许换行 */
        overflow    : hidden !important;
        line-break  : anywhere !important;
      }

      [class*="left-column"] > .flex {
        display : none !important;
      }
		`);
	}
}

// $(main);
// async function main(){
// }

// $(window).on('load', onLoaded);
// async function onLoaded(){
// }

/**
 * 生成一个范围数组，可以处理数字和字符范围。
 * @param {number|string} start - 范围的起始值。
 * @param {number|string} [end] - 范围的结束值。如果未提供，则默认从 0 开始。当 start 为字符串时，end 必须也为字符串。
 * @param {Object|boolean|number} [options] - 配置选项，或直接传递布尔值或步长。
 * @param {number} [options.step=1] - 步长，默认为 1。
 * @param {boolean} [options.eq=false] - 是否包含结束值，默认为 false。
 * @returns {Array<number|string>} - 返回生成的范围数组。
 */
function range(start, end, options){
	// 检查字符串情况
	let isChar = false;
	if (typeof start==='string'){
		if (typeof end!=='string'){
			throw new Error('当 start 为字符串时，end 必须为字符串');
		} else {
			isChar = true;
			start = start.charCodeAt(0);
			end = end.charCodeAt(0);
		}
	}
	
	// 只有一个参数的情况，默认从 0 开始
	if (end===undefined){
		end = start;
		start = 0;
	}
	
	// 解析选项options
	if (typeof options==='boolean'){
		options = {eq:options};
	} else if (typeof options==='number'){
		options = {step:options};
	} else if (typeof options==='undefined'){
		options = {};
	}
	const {step = 1, eq = false} = options;
	
	if (step===0){
		throw new Error('step 不能为 0');
	}
	
	let check;
	if (eq){
		if (step>0){
			check = (i)=>i<=end;
		} else {
			check = (i)=>i>=end;
		}
	} else {
		if (step>0){
			check = (i)=>i<end;
		} else {
			check = (i)=>i>end;
		}
	}
	
	const result = [];
	for (let i = start; check(i); i += step){
		result.push(i);
	}
	
	if (isChar){
		return result.map((i)=>String.fromCharCode(i));
	}
	return result;
}
